package org.xx.armory.swing.builders;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xml.sax.Attributes;
import org.xx.armory.objectparser.LineInfo;
import org.xx.armory.objectparser.NameObjectPair;
import org.xx.armory.objectparser.NameObjectPairBuilder;
import org.xx.armory.objectparser.impl.XMLFileParser;
import org.xx.armory.swing.components.BorderType;
import org.xx.armory.swing.components.TypedTableColumnType;

import javax.swing.*;
import java.util.Collection;

import static org.apache.commons.lang3.StringUtils.trimToEmpty;
import static org.xx.armory.xml.AttributesHelper.getBoolean;
import static org.xx.armory.xml.AttributesHelper.getInteger;
import static org.xx.armory.xml.AttributesHelper.getString;

public class XmlUIModelParser
        extends XMLFileParser<UIModel> {
    private static final String XSD_FILE_NAME = "classpath://ui.xsd";
    private static final int DEFAULT_UI_MARGIN = 4;
    private static final int DEFAULT_TABLE_MARGIN = 2;
    private final Logger logger = LoggerFactory.getLogger(XmlUIModelParser.class);

    /**
     * 构造{@link XmlUIModelParser}类型的实例。
     */
    public XmlUIModelParser() {
        setSchema(XSD_FILE_NAME);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected ElementHandler getHandler(
            Collection<? super UIModel> c
    ) {
        return new ElementHandler() {
            private StringBuilder content = new StringBuilder();

            @Override
            public void startElement(
                    LineInfo lineInfo,
                    String name,
                    Attributes attributes
            ) {
                this.content.delete(0, this.content.length());
                if ("ui".equals(name)) {
                    // 发现根节点。
                    enterUI(lineInfo, attributes);
                } else if ("message".equals(name)) {
                    enterMessage(lineInfo, attributes);
                } else if ("options".equals(name)) {
                    enterOptions(lineInfo, attributes);
                } else if ("standardAction".equals(name)) {
                    enterActionRef(lineInfo, attributes);
                } else if ("action".equals(name)) {
                    enterAction(lineInfo, attributes);
                } else if ("menuBar".equals(name)) {
                    enterMenuBar(lineInfo, attributes);
                } else if ("menu".equals(name)) {
                    enterMenu(lineInfo, attributes);
                } else if ("subMenu".equals(name)) {
                    enterMenu(lineInfo, attributes);
                } else if ("menuItem".equals(name)) {
                    enterActionRef(lineInfo, attributes);
                } else if ("toolBar".equals(name)) {
                    enterToolBar(lineInfo, attributes);
                } else if ("toolBarItem".equals(name)) {
                    enterActionRef(lineInfo, attributes);
                } else if ("boxLayout".equals(name)) {
                    enterBoxLayout(lineInfo, attributes);
                } else if ("gridLayout".equals(name)) {
                    enterGridLayout(lineInfo, attributes);
                } else if ("tabbedPane".equals(name)) {
                    enterTabbedPane(lineInfo, attributes);
                } else if ("tab".equals(name)) {
                    enterTab(lineInfo, attributes);
                } else if ("splitPane".equals(name)) {
                    enterSplitPane(lineInfo, attributes);
                } else if ("column".equals(name)) {
                    enterColumn(lineInfo, attributes);
                } else if ("row".equals(name)) {
                    enterRow(lineInfo, attributes);
                } else if ("cell".equals(name)) {
                    enterCell(lineInfo, attributes);
                } else if ("image".equals(name)) {
                    enterImage(lineInfo, attributes);
                } else if ("imageBox".equals(name)) {
                    enterImageBox(lineInfo, attributes);
                } else if ("literal".equals(name)) {
                    enterLiteral(lineInfo, attributes);
                } else if ("label".equals(name)) {
                    enterLabel(lineInfo, attributes);
                } else if ("button".equals(name)) {
                    enterButton(lineInfo, attributes);
                } else if ("text".equals(name)) {
                    enterText(lineInfo, attributes);
                } else if ("checkBox".equals(name)) {
                    enterCheckBox(lineInfo, attributes);
                } else if ("dropDown".equals(name)) {
                    enterDropDown(lineInfo, attributes);
                } else if ("listBox".equals(name)) {
                    enterListBox(lineInfo, attributes);
                } else if ("table".equals(name)) {
                    enterTable(lineInfo, attributes);
                } else if ("tableColumn".equals(name)) {
                    enterTableColumn(lineInfo, attributes);
                } else if ("option".equals(name)) {
                    enterOption(lineInfo, attributes);
                } else if ("separator".equals(name)) {
                    enterSeparator(lineInfo, attributes, false);
                } else if ("spring".equals(name)) {
                    enterSeparator(lineInfo, attributes, true);
                } else if ("ruler".equals(name)) {
                    enterRuler(lineInfo, attributes);
                } else if ("component".equals(name)) {
                    enterComponent(lineInfo, attributes);
                }
            }

            @Override
            public void text(
                    LineInfo lineInfo,
                    String text
            ) {
                this.content.append(text);
            }

            @Override
            public void endElement(
                    LineInfo lineInfo,
                    String name
            ) {
                if ("ui".equals(name)) {
                    c.add(exitUI());
                } else if ("message".equals(name)) {
                    final MessageModel model = exitMessage(this.content.toString());
                    ((UIModelBuilder) peek()).addMessage(model.getName(), model.getValue());
                } else if ("options".equals(name)) {
                    final OptionsModel model = exitOptions();
                    ((UIModelBuilder) peek()).addOptions(model);
                } else if ("standardAction".equals(name)) {
                    final String actionRef = exitActionRef();
                    ((UIModelBuilder) peek()).addStandardAction(actionRef);
                } else if ("action".equals(name)) {
                    final ActionModel model = exitAction();
                    ((UIModelBuilder) peek()).addAction(model);
                } else if ("menuBar".equals(name)) {
                    final MenuBarModel model = exitMenuBar();
                    ((UIModelBuilder) peek()).addMenuBar(model);
                } else if ("menu".equals(name)) {
                    final MenuModel model = exitMenu();
                    ((MenuBarModelBuilder) peek()).addMenu(model);
                } else if ("subMenu".equals(name)) {
                    final MenuModel model = exitMenu();
                    ((MenuModelBuilder) peek()).addSubMenu(model);
                } else if ("menuItem".equals(name)) {
                    final String actionRef = exitActionRef();
                    ((MenuModelBuilder) peek()).addActionRef(actionRef);
                } else if ("toolBar".equals(name)) {
                    final ToolBarModel model = exitToolBar();
                    ((UIModelBuilder) peek()).addTooBar(model);
                } else if ("toolBarItem".equals(name)) {
                    final String actionRef = exitActionRef();
                    ((ToolBarModelBuilder) peek()).addActionRef(actionRef);
                } else if ("boxLayout".equals(name)) {
                    final BoxLayoutModel model = exitBoxLayout();
                    ((ContainerModelBuilder) peek()).addChild(model);
                } else if ("gridLayout".equals(name)) {
                    final GridLayoutModel model = exitGridLayout();
                    ((ContainerModelBuilder) peek()).addChild(model);
                } else if ("tabbedPane".equals(name)) {
                    final TabbedPaneModel model = exitTabbedPane();
                    ((ContainerModelBuilder) peek()).addChild(model);
                } else if ("tab".equals(name)) {
                    final TabModel model = exitTab();
                    ((TabbedPaneModelBuilder) peek()).addTab(model);
                } else if ("splitPane".equals(name)) {
                    final SplitPaneModel model = exitSplitPane();
                    ((ContainerModelBuilder) peek()).addChild(model);
                } else if ("column".equals(name)) {
                    final ColumnModel model = exitColumn();
                    ((GridLayoutModelBuilder) peek()).addColumn(model);
                } else if ("row".equals(name)) {
                    final RowModel model = exitRow();
                    ((GridLayoutModelBuilder) peek()).addRow(model);
                } else if ("cell".equals(name)) {
                    final CellModel model = exitCell();
                    ((RowModelBuilder) peek()).addCell(model);
                } else if ("image".equals(name)) {
                    final ImageModel model = exitImage();
                    ((UIModelBuilder) peek()).addImage(model);
                } else if ("imageBox".equals(name)) {
                    final ImageBoxModel model = exitImageBox();
                    ((ContainerModelBuilder) peek()).addChild(model);
                } else if ("literal".equals(name)) {
                    final LabelModel model = exitLiteral(this.content.toString());
                    ((ContainerModelBuilder) peek()).addChild(model);
                } else if ("label".equals(name)) {
                    final LabelModel model = exitLabel(this.content.toString());
                    ((ContainerModelBuilder) peek()).addChild(model);
                } else if ("button".equals(name)) {
                    final ButtonModel model = exitButton();
                    ((ContainerModelBuilder) peek()).addChild(model);
                } else if ("text".equals(name)) {
                    final TextModel model = exitText();
                    ((ContainerModelBuilder) peek()).addChild(model);
                } else if ("checkBox".equals(name)) {
                    final CheckBoxModel model = exitCheckBox();
                    ((ContainerModelBuilder) peek()).addChild(model);
                } else if ("dropDown".equals(name)) {
                    final DropDownModel model = exitDropDown();
                    ((ContainerModelBuilder) peek()).addChild(model);
                } else if ("listBox".equals(name)) {
                    final ListBoxModel model = exitListBox();
                    ((ContainerModelBuilder) peek()).addChild(model);
                } else if ("table".equals(name)) {
                    final TableModel model = exitTable();
                    ((ContainerModelBuilder) peek()).addChild(model);
                } else if ("tableColumn".equals(name)) {
                    final TableColumnModel model = exitTableColumn();
                    ((TableModelBuilder) peek()).addColumn(model);
                } else if ("option".equals(name)) {
                    final OptionModel model = exitOption(this.content.toString());
                    ((OptionsContainerBuilder) peek()).addOption(model);
                } else if ("separator".equals(name)) {
                    final SeparatorModel model = exitSeparator();
                    ((ContainerModelBuilder) peek()).addChild(model);
                } else if ("spring".equals(name)) {
                    final SeparatorModel model = exitSeparator();
                    ((ContainerModelBuilder) peek()).addChild(model);
                } else if ("ruler".equals(name)) {
                    final RulerModel model = exitRuler();
                    ((ContainerModelBuilder) peek()).addChild(model);
                } else if ("component".equals(name)) {
                    final PlaceholderModel model = exitComponent();
                    ((ContainerModelBuilder) peek()).addChild(model);
                }
            }

        };
    }

    private void enterUI(
            LineInfo lineInfo,
            Attributes attributes
    ) {
        push(new UIModelBuilder()
                     .setTitle(getString(attributes, "title"))
                     .setWidth(getInteger(attributes, "width", -1))
                     .setHeight(getInteger(attributes, "height", -1))
                     .setIcon(getString(attributes, "icon"))
                     .setMargin(getInteger(attributes, "margin", DEFAULT_UI_MARGIN))
                     .setLineInfo(lineInfo));

        logger.trace("parsing ui ...");
    }

    private UIModel exitUI() {
        final UIModel r = ((UIModelBuilder) pop()).build();

        logger.trace("parsed ui");

        return r;
    }

    private void enterMessage(
            LineInfo lineInfo,
            Attributes attributes
    ) {
        push(new MessageModelBuilder()
                     .setName(getString(attributes, "name"))
                     .setLineInfo(lineInfo));

        logger.trace("  parsing message ...");
    }

    private MessageModel exitMessage(
            String value
    ) {
        final MessageModel r = ((MessageModelBuilder) pop()).setValue(value).build();

        logger.trace("  parsed message[name={}]", r.getName());

        return r;
    }

    private void enterAction(
            LineInfo lineInfo,
            Attributes attributes
    ) {
        push(new ActionModelBuilder()
                     .setName(getString(attributes, "name"))
                     .setTitle(getString(attributes, "title"))
                     .setMnemonic(getString(attributes, "mnemonic"))
                     .setAccelerator(getString(attributes, "accelerator"))
                     .setDescription(getString(attributes, "description"))
                     .setSmallIcon(getString(attributes, "smallIcon"))
                     .setEnabled(getBoolean(attributes, "enabled", true))
                     .setCauseVerification(getBoolean(attributes, "causeVerification", false))
                     .setCommand(getString(attributes, "command"))
                     .setLineInfo(lineInfo));

        logger.trace("  parsed action...");
    }

    private ActionModel exitAction() {
        final ActionModel r = ((ActionModelBuilder) pop()).build();

        logger.trace("  parsed action[name={}]", r.getName());

        return r;
    }

    private void enterOptions(
            LineInfo lineInfo,
            Attributes attributes
    ) {
        push(new OptionsModelBuilder()
                     .setName(getString(attributes, "name"))
                     .setLineInfo(lineInfo));

        logger.trace("  parsing options");
    }

    private OptionsModel exitOptions() {
        final OptionsModel r = ((OptionsModelBuilder) pop()).build();

        logger.trace("  parsed options");

        return r;
    }

    private void enterMenuBar(
            LineInfo lineInfo,
            Attributes attributes
    ) {
        push(new MenuBarModelBuilder().setLineInfo(lineInfo));
        logger.trace("  parsing menu-bar...");
    }

    private MenuBarModel exitMenuBar() {
        final MenuBarModel r = ((MenuBarModelBuilder) pop()).build();

        logger.trace("  parsed menu-bar");

        return r;
    }

    private void enterMenu(
            LineInfo lineInfo,
            Attributes attributes
    ) {
        push(new MenuModelBuilder()
                     .setTitle(getString(attributes, "title"))
                     .setMnemonic(getString(attributes, "mnemonic"))
                     .setLineInfo(lineInfo));
        logger.trace("  parsing menu ...");
    }

    private MenuModel exitMenu() {
        final MenuModel r = ((MenuModelBuilder) pop()).build();

        logger.trace("  parsed menu[name={}]", r.getTitle());

        return r;
    }

    private void enterToolBar(
            LineInfo lineInfo,
            Attributes attributes
    ) {
        push(new ToolBarModelBuilder());
        logger.trace("  parsing toolbar...");
    }

    private ToolBarModel exitToolBar() {
        final ToolBarModel r = ((ToolBarModelBuilder) pop()).build();

        logger.trace("  parsed toolbar");

        return r;
    }

    private void enterActionRef(
            LineInfo lineInfo,
            Attributes attributes
    ) {
        push(new NameObjectPairBuilder()
                     .setName(getString(attributes, "name"))
                     .setObject(getString(attributes, "value"))
                     .setLineInfo(lineInfo));
        logger.trace("  parsed action-ref ...");
    }

    private String exitActionRef() {
        final NameObjectPair r = ((NameObjectPairBuilder) pop()).build();

        logger.trace("  parsed action-ref[{}]", r.getObject());

        return r.getObject();
    }

    private void enterBoxLayout(
            LineInfo lineInfo,
            Attributes attributes
    ) {
        push(new BoxLayoutModelBuilder()
                     .setAxis(getAxis(getString(attributes, "axis", "X")))
                     .setHorizontalAlignment(getModelAlignment(getString(attributes, "horizontalAlignment", "BOTH")))
                     .setVerticalAlignment(getModelAlignment(getString(attributes, "verticalAlignment", "CENTER")))
                     .setTitle(getString(attributes, "title"))
                     .setBorder(getBorder(getString(attributes, "border", "NONE")))
                     .setName(getString(attributes, "name"))
                     .setVisible(getBoolean(attributes, "visible", true))
                     .setLineInfo(lineInfo));
        logger.trace("  parsing box-layout");
    }

    private BoxLayoutModel exitBoxLayout() {
        final BoxLayoutModel r = ((BoxLayoutModelBuilder) pop()).build();

        logger.trace("  parsed box-layout[{}]", r.getTitle());

        return r;
    }

    private void enterGridLayout(
            LineInfo lineInfo,
            Attributes attributes
    ) {
        push(new GridLayoutModelBuilder()
                     .setMargin(getInteger(attributes, "margin", DEFAULT_TABLE_MARGIN))
                     .setTitle(getString(attributes, "title"))
                     .setBorder(getBorder(getString(attributes, "border", "NONE")))
                     .setName(getString(attributes, "name"))
                     .setVisible(getBoolean(attributes, "visible", true))
                     .setLineInfo(lineInfo));
        logger.trace("  parsing grid-layout");
    }

    private GridLayoutModel exitGridLayout() {
        final GridLayoutModel r = ((GridLayoutModelBuilder) pop()).build();

        logger.trace("  parsed grid-layout[{}]", r.getTitle());

        return r;
    }

    private void enterTabbedPane(
            LineInfo lineInfo,
            Attributes attributes
    ) {
        push(new TabbedPaneModelBuilder()
                     .setName(getString(attributes, "name"))
                     .setVisible(getBoolean(attributes, "visible", true))
                     .setLineInfo(lineInfo));

        logger.trace("  parsing tabbed-pane");
    }

    private TabbedPaneModel exitTabbedPane() {
        final TabbedPaneModel r = ((TabbedPaneModelBuilder) pop()).build();

        logger.trace("  parsed tabbed-pane[{}]", r.getName());

        return r;
    }

    private void enterTab(
            LineInfo lineInfo,
            Attributes attributes
    ) {
        push(new TabModelBuilder()
                     .setTitle(getString(attributes, "title"))
                     .setDescription(getString(attributes, "description"))
                     .setLineInfo(lineInfo));

        logger.trace("  parsing tab");
    }

    private TabModel exitTab() {
        final TabModel r = ((TabModelBuilder) pop()).build();

        logger.trace("  parsed tab[{}]", r.getTitle());

        return r;
    }

    private void enterSplitPane(
            LineInfo lineInfo,
            Attributes attributes
    ) {
        push(new SplitPaneModelBuilder()
                     .setAxis(getAxis(getString(attributes, "axis", "X")))
                     .setDividerLocation(getDividerLocation(getString(attributes, "dividerLocation", "CENTER")))
                     .setName(getString(attributes, "name"))
                     .setVisible(getBoolean(attributes, "visible", true))
                     .setLineInfo(lineInfo));

        logger.trace("  parsing split-pane");
    }

    private SplitPaneModel exitSplitPane() {
        final SplitPaneModel r = ((SplitPaneModelBuilder) pop()).build();

        logger.trace("  parsed split-pane[{}]", r.getName());

        return r;
    }

    private void enterColumn(
            LineInfo lineInfo,
            Attributes attributes
    ) {
        push(new ColumnModelBuilder()
                     // 默认水平方向两端对齐。
                     .setAlignment(getModelAlignment(getString(attributes, "alignment", "BOTH")))
                     .setStretch(getInteger(attributes, "stretch", 100))
                     .setLineInfo(lineInfo));
        logger.trace("  parsing column");
    }

    private ColumnModel exitColumn() {
        final ColumnModel r = ((ColumnModelBuilder) pop()).build();

        logger.trace("  parsed column");

        return r;
    }

    private void enterRow(
            LineInfo lineInfo,
            Attributes attributes
    ) {
        push(new RowModelBuilder()
                     // 默认垂直方向居中对齐。
                     .setAlignment(getModelAlignment(getString(attributes, "alignment", "CENTER")))
                     // 默认不自动伸展
                     .setStretch(getInteger(attributes, "stretch", 0))
                     .setLineInfo(lineInfo));
        logger.trace("  parsing row");
    }

    private RowModel exitRow() {
        final RowModel r = ((RowModelBuilder) pop()).build();

        logger.trace("  parsed row");

        return r;
    }

    private void enterCell(
            LineInfo lineInfo,
            Attributes attributes
    ) {
        push(new CellModelBuilder()
                     .setHorizontalAlignment(getModelAlignment(getString(attributes, "horizontalAlignment")))
                     .setVerticalAlignment(getModelAlignment(getString(attributes, "verticalAlignment")))
                     .setRowSpan(getInteger(attributes, "rowSpan", 1))
                     .setColumnSpan(getInteger(attributes, "columnSpan", 1))
                     .setMargin(getInteger(attributes, "margin", -1))
                     .setLineInfo(lineInfo));
        logger.trace("  parsing cell");
    }

    private CellModel exitCell() {
        final CellModel r = ((CellModelBuilder) pop()).build();

        logger.trace("  parsed cell");

        return r;
    }

    private void enterLiteral(
            LineInfo lineInfo,
            Attributes attributes
    ) {
        push(new LabelModelBuilder()
                     .setAlignment(getModelAlignment(getString(attributes, "alignment", "FIRST")))
                     .setHtml(getBoolean(attributes, "html", false))
                     .setName(getString(attributes, "name"))
                     .setVisible(getBoolean(attributes, "visible", true))
                     .setLineInfo(lineInfo));
        logger.trace("  parsing literal");
    }

    private LabelModel exitLiteral(
            String content
    ) {
        final LabelModel r = ((LabelModelBuilder) pop())
                .setText(trimToEmpty(content))
                .build();

        logger.trace("  parsed literal");

        return r;
    }

    private void enterLabel(
            LineInfo lineInfo,
            Attributes attributes
    ) {
        push(new LabelModelBuilder()
                     .setAlignment(getModelAlignment(getString(attributes, "alignment", "FIRST")))
                     .setMnemonic(getString(attributes, "mnemonic"))
                     .setForName(getString(attributes, "for"))
                     .setName(getString(attributes, "name"))
                     .setVisible(getBoolean(attributes, "visible", true))
                     .setLineInfo(lineInfo));
        logger.trace("  parsing label");
    }

    private LabelModel exitLabel(
            String content
    ) {
        final LabelModel r = ((LabelModelBuilder) pop())
                .setText(trimToEmpty(content))
                .build();

        logger.trace("  parsed label[{}]", r.getName());

        return r;
    }

    private void enterImage(
            LineInfo lineInfo,
            Attributes attributes
    ) {
        push(new ImageModelBuilder()
                     .setSrc(getString(attributes, "src"))
                     .setName(getString(attributes, "name"))
                     .setLineInfo(lineInfo));
        logger.trace("  parsing image");
    }

    private ImageModel exitImage() {
        final ImageModel r = ((ImageModelBuilder) pop()).build();

        logger.trace("  parsed image[{}]", r.getName());

        return r;
    }

    private void enterImageBox(
            LineInfo lineInfo,
            Attributes attributes
    ) {
        push(new ImageBoxModelBuilder()
                     .setSrc(getString(attributes, "src"))
                     .setImageName(getString(attributes, "imageName"))
                     .setName(getString(attributes, "name"))
                     .setVisible(getBoolean(attributes, "visible", true))
                     .setLineInfo(lineInfo));
        logger.trace("  parsing image-box");
    }

    private ImageBoxModel exitImageBox() {
        final ImageBoxModel r = ((ImageBoxModelBuilder) pop()).build();

        logger.trace("  parsed image-box[{}]", r.getName());

        return r;
    }

    private void enterButton(
            LineInfo lineInfo,
            Attributes attributes
    ) {
        push(new ButtonModelBuilder()
                     .setActionName(getString(attributes, "actionName"))
                     .setDefault(getBoolean(attributes, "default", false))
                     .setName(getString(attributes, "name"))
                     .setVisible(getBoolean(attributes, "visible", true))
                     .setLineInfo(lineInfo));
        logger.trace("  parsing button");
    }

    private ButtonModel exitButton() {
        final ButtonModel r = ((ButtonModelBuilder) pop()).build();

        logger.trace("  parsed button[{}]", r.getName());

        return r;
    }

    private void enterText(
            LineInfo lineInfo,
            Attributes attributes
    ) {
        push(new TextModelBuilder()
                     .setType(getTextModelType(getString(attributes, "type", "TEXT")))
                     .setSize(getInteger(attributes, "size", 16))
                     .setRowSize(getInteger(attributes, "rowSize", 1))
                     .setRequired(getBoolean(attributes, "required", false))
                     .setMaxLength(getInteger(attributes, "maxLength", -1))
                     .setMinValue(getString(attributes, "minValue"))
                     .setMaxValue(getString(attributes, "maxValue"))
                     .setPattern(getString(attributes, "pattern"))
                     .setPrefix(getString(attributes, "prefix"))
                     .setSuffix(getString(attributes, "suffix"))
                     .setOptionsName(getString(attributes, "optionsName"))
                     .setName(getString(attributes, "name"))
                     .setVisible(getBoolean(attributes, "visible", true))
                     .setLineInfo(lineInfo));
        logger.trace("  parsing text");
    }

    private TextModel exitText() {
        final TextModel r = ((TextModelBuilder) pop()).build();

        logger.trace("  parsed text[{}]", r.getName());

        return r;
    }

    private void enterCheckBox(
            LineInfo lineInfo,
            Attributes attributes
    ) {
        push(new CheckBoxModelBuilder()
                     .setText(getString(attributes, "text"))
                     .setName(getString(attributes, "name"))
                     .setVisible(getBoolean(attributes, "visible", true))
                     .setLineInfo(lineInfo));
        logger.trace("   parsing checkbox");
    }

    private CheckBoxModel exitCheckBox() {
        final CheckBoxModel r = ((CheckBoxModelBuilder) pop()).build();

        logger.trace("  parsed checkbox");

        return r;
    }

    private void enterDropDown(
            LineInfo lineInfo,
            Attributes attributes
    ) {
        push(new DropDownModelBuilder()
                     .setIncludeAll(getBoolean(attributes, "includeAll", false))
                     .setIncludeNone(getBoolean(attributes, "includeNone", false))
                     .setSize(getInteger(attributes, "size", 0))
                     .setRequired(getBoolean(attributes, "required", false))
                     .setOptionsName(getString(attributes, "optionsName"))
                     .setName(getString(attributes, "name"))
                     .setVisible(getBoolean(attributes, "visible", true))
                     .setLineInfo(lineInfo));

        logger.trace("  parsing combo-box");
    }

    private DropDownModel exitDropDown() {
        final DropDownModel r = ((DropDownModelBuilder) pop()).build();

        logger.trace("  parsed combo-box");

        return r;
    }

    private void enterListBox(
            LineInfo lineInfo,
            Attributes attributes
    ) {
        push(new ListBoxModelBuilder()
                     .setSize(getInteger(attributes, "size", 16))
                     .setRowSize(getInteger(attributes, "rowSize", 8))
                     .setName(getString(attributes, "name"))
                     .setVisible(getBoolean(attributes, "visible", true))
                     .setLineInfo(lineInfo));

        logger.trace("  parsing list-box");
    }

    private ListBoxModel exitListBox() {
        final ListBoxModel r = ((ListBoxModelBuilder) pop()).build();

        logger.trace("  parsed list-box");

        return r;
    }

    private void enterTable(
            LineInfo lineInfo,
            Attributes attributes
    ) {
        push(new TableModelBuilder()
                     .setRowSize(getInteger(attributes, "rowSize", 8))
                     .setName(getString(attributes, "name"))
                     .setVisible(getBoolean(attributes, "visible", true))
                     .setLineInfo(lineInfo));

        logger.trace("  parsing table");
    }

    private TableModel exitTable() {
        final TableModel r = ((TableModelBuilder) pop()).build();

        logger.trace("  parsed table");

        return r;
    }

    private void enterTableColumn(
            LineInfo lineInfo,
            Attributes attributes
    ) {
        push(new TableColumnModelBuilder()
                     .setName(getString(attributes, "name"))
                     .setTitle(getString(attributes, "title"))
                     .setSize(getInteger(attributes, "size", 120))
                     .setType(getTypedTableColumnType(getString(attributes, "type")))
                     .setOptionsName(getString(attributes, "optionsName"))
                     .setLineInfo(lineInfo));

        logger.trace("  parsing table");
    }

    private TableColumnModel exitTableColumn() {
        final TableColumnModel r = ((TableColumnModelBuilder) pop()).build();

        logger.trace("  parsed table-column");

        return r;
    }

    private void enterOption(
            LineInfo lineInfo,
            Attributes attributes
    ) {
        push(new OptionModelBuilder()
                     .setValue(getString(attributes, "value"))
                     .setLineInfo(lineInfo));

        logger.trace("  parsing option");
    }

    private OptionModel exitOption(
            String text
    ) {
        final OptionModel r = ((OptionModelBuilder) pop()).setText(text).build();

        logger.trace("  parsed option");

        return r;
    }

    private void enterSeparator(
            LineInfo lineInfo,
            Attributes attributes,
            boolean spring
    ) {
        push(new SeparatorModelBuilder()
                     .setSpring(spring)
                     .setLineInfo(lineInfo));
        logger.trace("  parsing separator");
    }

    private SeparatorModel exitSeparator() {
        final SeparatorModel r = ((SeparatorModelBuilder) pop()).build();

        logger.trace("  parsed separator");

        return r;
    }

    private void enterRuler(
            LineInfo lineInfo,
            Attributes attributes
    ) {
        push(new RulerModelBuilder()
                     .setAxis(getAxis(getString(attributes, "axis", "X")))
                     .setType(getBorder(getString(attributes, "type", "LOWERED")))
                     .setName(getString(attributes, "name"))
                     .setVisible(getBoolean(attributes, "visible", true))
                     .setLineInfo(lineInfo));

        logger.trace("  parsing ruler");
    }

    private RulerModel exitRuler() {
        final RulerModel r = ((RulerModelBuilder) pop()).build();

        logger.trace("  parsed ruler");

        return r;
    }

    private void enterComponent(
            LineInfo lineInfo,
            Attributes attributes
    ) {
        push(new PlaceholderModelBuilder()
                     .setClassName(getString(attributes, "className"))
                     .setScrollable(getBoolean(attributes, "scrollable", true))
                     .setName(getString(attributes, "name"))
                     .setVisible(getBoolean(attributes, "visible", true))
                     .setLineInfo(lineInfo));
        logger.trace("  parsing component");
    }

    private PlaceholderModel exitComponent() {
        final PlaceholderModel r = ((PlaceholderModelBuilder) pop()).build();

        logger.trace("  parsed component");

        return r;
    }

    private ModelAlignment getModelAlignment(
            String value
    ) {
        switch (value) {
            case "":
            case "NONE":
                return ModelAlignment.NONE;
            case "FIRST":
                return ModelAlignment.FIRST;
            case "LAST":
                return ModelAlignment.LAST;
            case "CENTER":
                return ModelAlignment.CENTER;
            case "BOTH":
                return ModelAlignment.BOTH;
            default:
                throw new IllegalArgumentException("unknown alignment: " + value);
        }
    }

    private TextModelType getTextModelType(
            String value
    ) {
        switch (value) {
            case "TEXT":
                return TextModelType.TEXT;
            case "PASSWORD":
                return TextModelType.PASSWORD;
            case "TEXT_AREA":
                return TextModelType.TEXT_AREA;
            case "NUMBER":
                return TextModelType.NUMBER;
            case "PERCENT":
                return TextModelType.PERCENT;
            case "CURRENCY":
                return TextModelType.CURRENCY;
            case "DATE":
                return TextModelType.DATE;
            case "DATE_TIME":
                return TextModelType.DATE_TIME;
            default:
                throw new IllegalArgumentException("illegal text type: " + value);
        }
    }

    private BorderType getBorder(
            String value
    ) {
        switch (value) {
            case "NONE":
                return BorderType.NONE;
            case "EMPTY":
                return BorderType.EMPTY;
            case "SOLID":
                return BorderType.SOLID;
            case "LOWERED":
                return BorderType.LOWERED;
            case "RAISED":
                return BorderType.RAISED;
            default:
                throw new IllegalArgumentException("illegal border: " + value);
        }
    }

    private int getAxis(
            String axis
    ) {
        switch (axis) {
            case "X":
                return BoxLayout.X_AXIS;
            case "Y":
                return BoxLayout.Y_AXIS;
            default:
                throw new IllegalArgumentException("illegal axis: " + axis);
        }
    }

    private int getDividerLocation(
            String dividerLocation
    ) {
        switch (dividerLocation) {
            case "FIRST":
                return SplitPaneModel.DIVIDER_FIRST;
            case "CENTER":
                return SplitPaneModel.DIVIDER_CENTER;
            case "LAST":
                return SplitPaneModel.DIVIDER_LAST;
            default:
                throw new IllegalArgumentException("illegal divider location: " + dividerLocation);
        }
    }

    private TypedTableColumnType getTypedTableColumnType(
            String value
    ) {
        switch (value) {
            case "TEXT":
                return TypedTableColumnType.TEXT;
            case "NUMBER":
                return TypedTableColumnType.NUMBER;
            case "NUMBER_TEXT":
                return TypedTableColumnType.NUMBER_TEXT;
            case "FLOAT":
                return TypedTableColumnType.FLOAT;
            case "PERCENT":
                return TypedTableColumnType.PERCENT;
            case "CURRENCY":
                return TypedTableColumnType.CURRENCY;
            case "DATE":
                return TypedTableColumnType.DATE;
            case "DATE_TIME":
                return TypedTableColumnType.DATE_TIME;
            case "YES_OR_NO":
                return TypedTableColumnType.YES_OR_NO;
            case "CHECK_BOX":
                return TypedTableColumnType.CHECK_BOX;
            case "IMAGE":
                return TypedTableColumnType.IMAGE;
            default:
                throw new IllegalArgumentException("unknown column type: " + value);
        }
    }
}
